package com.xwj.thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 我们提供一个类：
 * <p>
 * class FooBar {
 * public void foo() {
 *     for (int i = 0; i < n; i++) {
 *       print("foo");
 *     }
 * }
 * <p>
 * public void bar() {
 *     for (int i = 0; i < n; i++) {
 *       print("bar");
 *     }
 * }
 * }
 * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法，另一个线程将会调用 bar() 方法。
 * <p>
 * 请设计修改程序，以确保 "foobar" 被输出 n 次。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/print-foobar-alternately
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 *
 * @author xwj
 * @date 2021/2/27
 */
public class FooBar {
    private int n;
    private ReentrantLock lock = new ReentrantLock();
    private Condition one = lock.newCondition();
    private Condition two = lock.newCondition();
    private Integer flag = 0;

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {

            // printFoo.run() outputs "foo". Do not change or remove this line.

            lock.lock();
            try {
                if (flag != 0) {
                    one.await();
                }
                printFoo.run();
                flag = 1;
                two.signal();
            } finally {
                lock.unlock();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {

            // printBar.run() outputs "bar". Do not change or remove this line.
            lock.lock();
            try {
                if (flag != 1) {
                    two.await();
                }
                printBar.run();
                flag = 0;
                one.signal();
            } finally {
                lock.unlock();
            }
        }
    }
}

/**
 * 解法2：BlockingQueue
 */
class FooBar1 {
    private int n;
    private BlockingQueue<String> foo = new LinkedBlockingQueue<>(1);
    private BlockingQueue<String> bar = new LinkedBlockingQueue<>(1);

    public FooBar1(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {

            // printFoo.run() outputs "foo". Do not change or remove this line.

            foo.put("start bar");
            printFoo.run();
            bar.put("start foo");
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {

            // printBar.run() outputs "bar". Do not change or remove this line.
            bar.take();
            printBar.run();
            foo.take();
        }
    }

}

